package com.swf.seed.netty.client.client;

import java.net.InetSocketAddress;
import java.util.concurrent.CountDownLatch;

import org.jboss.netty.bootstrap.ClientBootstrap;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelFactory;
import org.jboss.netty.channel.ChannelHandler;
import org.jboss.netty.channel.ChannelLocal;
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.channel.Channels;
import org.jboss.netty.channel.SimpleChannelHandler;
import org.jboss.netty.channel.group.ChannelGroup;
import org.jboss.netty.channel.group.DefaultChannelGroup;
import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.ObjectUtils;

import com.swf.seed.netty.api.ClientRequest;
import com.swf.seed.netty.api.ClientResponse;
import com.swf.seed.netty.common.ChannelPool;
import com.swf.seed.netty.common.DefaultChannelPool;

public class SeedClient extends SimpleChannelHandler{
	private static final Logger log = LoggerFactory
			.getLogger(SeedClient.class);
	private String host;
	private int port;
	private int poolSize = 1;
	private ClientBootstrap bootstrap;
	private ChannelPool channelPool;
	private ChannelHandler[] codecHandlers;
	
	public SeedClient(String host, int port) {
		this.host = host;
		this.port = port;
	}
	public void setChannelPool(ChannelPool channelPool) {
		this.channelPool = channelPool;
	}
	public ClientBootstrap getBootstrap() {
		return bootstrap;
	}
	public void setBootstrap(ClientBootstrap bootstrap) {
		this.bootstrap = bootstrap;
	}
	public ChannelHandler[] getCodecHandlers() {
		return codecHandlers;
	}
	public void setCodecHandlers(ChannelHandler[] codecHandlers) {
		this.codecHandlers = codecHandlers;
	}
	public ChannelPool getChannelPool() {
		return channelPool;
	}


	//
	private ChannelLocal<ResultHandler> resultHandler = new ChannelLocal<ResultHandler>();

	public void startup() {
		bootstrap = new ClientBootstrap();
		bootstrap.setFactory(getChannelFactory());
		bootstrap.setPipelineFactory(getPipelineFactory());
		// options
		bootstrap.setOption("tcpNoDelay", true);
		bootstrap.setOption("keepAlive", true);
		//
		ChannelGroup channelGroup = new DefaultChannelGroup();
		for (int i = 0; i < poolSize; i++) {
			Channel channel = bootstrap
					.connect(new InetSocketAddress(host, port))
					.awaitUninterruptibly().getChannel();
			channelGroup.add(channel);
		}
		//
		channelPool = new DefaultChannelPool(channelGroup);
	}
	
	public ClientResponse send(ClientRequest clientRequest) {
        //
        try {
            Channel channel = channelPool.borrowChannel();
            //
            final ResultHandler resultHandler = new ResultHandler();
            synchronized (resultHandler) {
                this.resultHandler.set(channel, resultHandler);
                channel.write(clientRequest);
            }
            //
            ClientResponse clientResponse = (ClientResponse) resultHandler.get();
            channelPool.returnChannel(channel);
            return clientResponse;
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            return null;
        }
    }
	
	
	public void shutdown() {
		bootstrap.shutdown();
	}

	protected ChannelFactory getChannelFactory() {
		return new NioClientSocketChannelFactory();
	}

	protected ChannelPipelineFactory getPipelineFactory() {
		final ChannelHandler[] channelHandlers = ObjectUtils.addObjectToArray(
				codecHandlers, this);
		//
		return new ChannelPipelineFactory() {
			public ChannelPipeline getPipeline() throws Exception {
				return Channels.pipeline(channelHandlers);
			}
		};
	}

	private static class ResultHandler {

		private Object value;
		private CountDownLatch latch = new CountDownLatch(1);

		public void handle(Object value) {
			this.value = value;
			this.latch.countDown();
		}

		public Object get() {
			try {
				latch.await();
			} catch (InterruptedException e) {
				e.printStackTrace();
				// log.error(e.getMessage(), e);
			}
			return value;
		}
	}

}
